package com.zhuqi.netty.properties;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.zhuqi.anno.NettyPropertiesNotEmpty;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;

/**
 * <p>客户端配置</p>
 * @author zhuqi
 * @since 2023/3/27 11:18
 */
@Component
@Data
@Slf4j
public class NettyClientProperties implements ApplicationContextAware {

	/**
	 * ip
	 */
	@Value("${netty.clinet.ip}")
	@NettyPropertiesNotEmpty
	private String ip;

	/**
	 * 端口
	 */
	@Value("${netty.clinet.port}")
	@NettyPropertiesNotEmpty
	private Integer port;

	/**
	 * 重连次数
	 */
	@Value("${netty.clinet.reConnectCount}")
	@NettyPropertiesNotEmpty
	private Integer reConnectCount;

	@Value("${netty.session.expireTime}")
	@NettyPropertiesNotEmpty
	private Integer expireTime;

	@Value("${netty.serializeCode}")
	@NettyPropertiesNotEmpty
	private Integer serializeCode;

	@Value("${netty.event-loop.boss.thread-count}")
	private Integer bossThreadCount;

	@Value("${netty.event-loop.worker.thread-count}")
	private Integer workerThreadCount;

	@Value("${netty.event-loop.default.thread-count}")
	private Integer defaultThreadCount;

	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	@SneakyThrows
	@PostConstruct
	private void checkParam() {
		NettyClientProperties bean = applicationContext.getBean(NettyClientProperties.class);
		Field[] declaredFields = bean.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			NettyPropertiesNotEmpty annotation = field.getAnnotation(NettyPropertiesNotEmpty.class);
			if (ObjectUtil.isNotEmpty(annotation)) {
				doCheckParam(bean, field);
			}
		}

		initThreadCount();
	}

	/**
	 * 初始化 Netty EventGroupLoop 线程数
	 */
	private void initThreadCount() {
		int availableProcessors = Runtime.getRuntime().availableProcessors();
		if (bossThreadCount == null) {
			bossThreadCount = 2;
		}
		if (workerThreadCount == null) {
			workerThreadCount = availableProcessors - 1;
		}
		if (defaultThreadCount == null) {
			defaultThreadCount = availableProcessors - 1;
		}
		int maxCount = availableProcessors << 2;
		if (bossThreadCount >=  maxCount) {
			log.info("Accepted EventLoop线程数过大, 进行合适化调整！");
			bossThreadCount = availableProcessors <= 2 ? 1 : 2;
		}
		if (workerThreadCount >=  maxCount || defaultThreadCount >= maxCount) {
			log.info("Accepted EventLoop线程数过大, 进行合适化调整！");
			workerThreadCount = availableProcessors - 1;
			defaultThreadCount = availableProcessors - 1;
		}
	}

	/**
	 * Netty 参数检查
	 * @param bean 配置bean
	 * @param field 属性值
	 * @throws IllegalAccessException 参数为空异常
	 */
	private void doCheckParam(NettyClientProperties bean, Field field) throws IllegalAccessException {
		Object data = field.get(bean);
		Assert.isTrue(ObjectUtil.isNotEmpty(data), StrUtil.format("netty配置{}:{} 数据为空", field.getName(), data));
	}
}
